
// 装饰器 可以不修改类的情况下扩展类的功能
// ! 普通装饰器 ( 无法传参 )
console.log(' 普通装饰器 ( 无法传参 )');

function logClass(params: any) {
    console.log(params);  // 这里打印的是当前类的constructor

    params.prototype.apiUrl = 'xxx' // 动态扩展的属性

    params.prototype.run = function () {
        console.log('run fn');

    }

}
@logClass
class httpClient {
    constructor() {

    }
    getData() {

    }
}

const http1: any = new httpClient();
console.log(http1.apiUrl); // 这里打印出了xxx
http1.run() // run fn

// ? ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

// ! 类装饰器:装饰器工厂 (可以传参)
console.log('类装饰器:装饰器工厂 (可以传参)');

function logClass2(params: string) {

    return function (target: any) {
        console.log('target:');
        console.log(target); // constructor node环境打印不了 / 浏览器可以
        console.log('params:');
        console.log(params); // 传进来的参数

        target.prototype.apiUrl = 'api url xxx'
    }
}

@logClass2('asd')
class HttpClient2 {
    constructor() {

    }
    getData() {

    }
}
const http2: any = new HttpClient2();
console.log('http2.apiUrl: ', http2.apiUrl);

// ? ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

// ! 类装饰器: 重载类
console.log('类装饰器: 重载类');

function logClass3(target: any) {
    //重载的话是返回一个class并继承自身的constructor
    return class extends target {
        apiUrl: string = '我是修改后的 in HttpClient3'
        getData() {
            console.log('重载.apiUrl', this.apiUrl);

        }
    }

}


@logClass3
class HttpClient3 {
    public apiUrl: string | undefined;
    constructor() {
        this.apiUrl = 'in HttpClient3'
    }
    getData(): void {
        console.log(this.apiUrl);
    }
}

const http3 = new HttpClient3()
http3.getData()

// ? ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

// ! 属性装饰器
console.log('属性装饰器');

function logClass4(params: string) {
    return function (target: any) {
        // console.log('params: ', params);
        // console.log('target: ', target);
    }
}

function logProperty(params: string) {
    return function (target: any, attr: any) {
        console.log('attr: ', attr); // 类的属性 其实就是 url 单个属性
        console.log('params: ', params);
        console.log('target: ', target); // class 本身
        target[attr] = params; // 这里必须用数组[ ] 不然会搞去.attr 而不是url
    }
}

@logClass4('zxczxc')
class HttpClient4 {
    @logProperty('xxxxx1x')
    public url: string | undefined;
    constructor() {

    }
    getData() {
        console.log('this.url: ', this.url);
    }

}

const http4 = new HttpClient4();
http4.getData()


// ? ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

// ! 属性装饰器(方法)
console.log('属性装饰器(方法)');

function logProperty2(params: string) {
    return function (target: any, attr: any, desc: any) {
        console.log('desc: ', desc); // 方法的描述
        console.log('attr: ', attr); // 方法的名称
        console.log('target: ', target); // class本身
        // target.run()=>{} // 这里可以扩展
        const thatMethod = desc.value; // 保存当前方法
        desc.value = function (...args: Array<any>) { // 修改装饰器弄的那个方法
            console.log('args: ', args);
            console.log('this: ', this);
            // 在这里调用方法本身;
            thatMethod.apply(this, args)
        }
    }
}
class HttpClient5 {
    public url: string | undefined;
    constructor() {

    }
    @logProperty2('xxxxx1x')
    getData(...data: any[]) {
        console.log('getData 自身: ', data);
    }

}

const http5 = new HttpClient5();
console.log('HttpClient5: ', HttpClient5);
http5.getData('abc', 123);

// ? ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

// ! 方法参数装饰器 (不常用)
console.log('方法参数装饰器 (不常用)');

function logParams(params: any) {
    console.log('params: ', params);
    return function (target: any, methodName: any, paramsIndex: any) {
        console.log('target: ', target);//类的原型对象
        console.log('methodName: ', methodName);//方法名
        console.log('paramsIndex: ', paramsIndex);//下表

        target.biubiu = params
    }
}

class HttpClient6 {
    public url: string | undefined;
    constructor() {

    }

    getData(@logParams('uuid') uuid: any) {
        console.log('uuid: ', uuid);
    }

}

const http6: any = new HttpClient6();
http6.getData('123');

console.log('http6.biubiu: ', http6.biubiu);




// ? ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

// ! 装饰器-执行顺序
console.log('装饰器-执行顺序');  // ! 顺序 -> 属性 > 方法 > 方法参数 > 类 ( 多个的话是从后到前 )
function cla1(params: any) {
    return function (target: any) {
        console.log('类装饰器-cla1');

    }
}
function cla2(params: any) {
    return function (target: any) {
        console.log('类装饰器-cla2');

    }
}

function attr1(params?: any) { // 这里是可以做可选参数的
    return function (target: any, attrName: any) {
        console.log('属性装饰器');

    }
}

function meth(params?: string) {
    return function (target: any, attrName: any, desc: any) {
        console.log('方法装饰器');

    }
}

function params1(params?: string) {
    return function (target: any, methodName: any, paramsIndex: any) {
        console.log('参数装饰器1');

    }
}

function params2(params?: string) {
    return function (target: any, methodName: any, paramsIndex: any) {
        console.log('参数装饰器2');

    }
}

@cla1('asd1')
@cla2('asd2')
class HttpClient7 {
    @attr1()
    public biubiu: string | undefined
    constructor() {

    }
    @meth()
    getData() {

    }

    setData(@params1() data: any, @params2() data2: any) { //参数装饰器执行顺序是从后向前

    }
}



